Flume UDP Source丢包问题

问题描述

有用户提出通过udp协议发送数据到实时平台,所以考虑在flume接收节点添加udp source来接收udp请求

  • flume配置如下
1
2
3
4
a2.sources.syslog_udp_src.type=syslogudp
a2.sources.syslog_udp_src.host=0.0.0.0
a2.sources.syslog_udp_src.port=5340
a2.sources.syslog_udp_src.channels=test_channel
  • 系统环境如下
1
2
3
CentOS Linux release 7.2.1511
4核cpu
8G内存

在测试中发现当发送速度超过500/s时开始出现数据丢失,开始以为是网络丢包,但通过tcpdump抓包比对发现并没有丢包

netstat -su命令显示有数据包接收失败

1
2
3
4
5
6
Udp:
4329216468 packets received
329761 packets to unknown port received.
301745872 packet receive errors
337569082 packets sent
RcvbufErrors: 1061789

  • packets received表示application接收到的数据包的数量
  • packets to unknown port表示由于端口没有打开而丢失的数据包数据量,例如应用挂掉或者重启
  • packet receive errors表示接收数据包的异常的数量,需要注意的是error和接收失败的数据包并不是一对一的关系,即有一个数据包有可能会产生多个error

问题原因

当客户端发送一个udp请求到flume时,整个流转顺序是这样的

1
Client -> Kernel UDP socket buffer(4MB) -> Flume UDP Source buffer(64KB) -> Flume Channel buffer(磁盘,file channel)

  • flume通过SyslogUDPSource接收UDP请求,底层是使用Netty的OioDatagramChannelFactory来创建服务器端channel,实现BIO(阻塞式IO)。BIO会对每个请求分配一个线程来处理,这种处理模式效率很低。经过测试,Flume UDP只能达到1100+的QPS,0.324MB/s

  • 当client的发送速度大于flume udp source的接收速度时,数据开始在Kernel UDP socket buffer堆积,当buffer满了的时候,后续的请求就会被丢弃,发生packet receive errors

  • 即使平均吞吐量没有超过flume udp的接收能力,但如果出现发送速度波动或者网络波动导致短时间内Kernel UDP socket buffer被填满,也会出现接收失败

解决方案

  • 网上看了一下,有的建议扩大Kernel UDP socket buffer,比如50MB
    1
    2
    sysctl -w net.core.rmem_max=52428800
    sysctl -w net.core.netdev_max_backlog=2000

但这只能在一定程度上避免波动的问题,如果发送速度大于接收速度,再大的缓存总会有满的时候

  • 用NIO重写flume SyslogUDPSource,提高处理效率,目前并没有这么做的打算,因为采取了下一个方案

  • 部署syslog-ng接收udp请求,并转发到flume tcp source。Syslog-ng是一个轻量框架,并不需要对数据进行过多的处理和缓存,可以直接对udp请求进行转发,并且使用的是多路复用的NIO,因此处理效率很高。即使是这样,经过压力测试发现也同样是有瓶颈,当qps大于24000时开始出现同样的packet receive errors,因为无论如何syslog-ng也是有处理上限的

    1
    2
    2.5W QPS,丢包率0.008%
    10W QPS,丢包率2.2%
  • 以上两种方案在数据量大到一定程度的时候都不可避免的丢包,所以如果可以的话,应该在数据发送之前就进行必要的过滤,把不需要的数据扔掉,降低请求数

  • 最终的解决方案,也是我最推崇的:

放弃UDP,改用TCP